using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.DirectoryServices;
using System.ServiceProcess;

namespace KodeIT.Web.Registration
{
    internal static class Iis
    {
        private const string IIsWebService = "IIsWebService";
        private const string IIsWebServer = "IIsWebServer";
        private const string IIsFilters = "IIsFilters";
        private const string IIsFilter = "IIsFilter";

        private const string AdsPath_IisRoot = "IIS://localhost/W3SVC";
        private const string AdsMethod_Create = "Create";
        private const string AdsMethod_Delete = "Delete";

        internal static void StopIis()
        {
            try
            {
                Console.Write("IIS: Wait 30s for IIS to stop ... ");
                ServiceController svc = new ServiceController("W3SVC");
                svc.Stop();
                svc.WaitForStatus(ServiceControllerStatus.Stopped, new TimeSpan(0, 0, 30));
                Console.WriteLine("stopped");
            }
            catch (Exception exp)
            {
                Console.WriteLine("\n     Error: " + exp.Message);
            }
            
        }

        internal static void StartIis()
        {
            try
            {
                Console.Write("IIS: Wait 30s for IIS to start ... ");
                ServiceController svc = new ServiceController("W3SVC");
                svc.Start();
                svc.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 30));
                Console.WriteLine("running");
            }
            catch (Exception exp)
            {
                Console.WriteLine("\n     Error: " + exp.Message);
            }
        }

        internal static void AddFilter(string filterName, string filterPath)
        {
            try
            {
                Console.Write("IIS: Add ISAPI Filter {0} to /LM/W3SVC ... ", filterName);
                
                DirectoryEntry webService = new DirectoryEntry(AdsPath_IisRoot);
                DirectoryEntry webFilters = GetFiltersNode(webService);
                RemoveFilterNode(webFilters, filterName);
                UnsetFilterName(webFilters, filterName);
                AddFilterNode(webFilters, filterName, filterPath);
                SetFilterName(webFilters, filterName);
                Console.WriteLine("done");
            }
            catch (Exception exp)
            {
                Console.WriteLine("\n     Error: " + exp.Message);
            }
        }

        internal static void RemoveFilter(string filterName, bool isRecursive)
        {
            try
            {
                Console.Write("IIS: Remove ISAPI Filter {0} from /LM/W3SVC ... ", 
                    filterName);

                DirectoryEntry webService = new DirectoryEntry(AdsPath_IisRoot);
                DirectoryEntry webFilters = GetFiltersNode(webService);
                RemoveFilterNode(webFilters, filterName);
                UnsetFilterName(webFilters, filterName);
                Console.WriteLine("done");

                if (isRecursive)
                {
                    foreach (DirectoryEntry child in webService.Children)
                    {
                        if (child.SchemaClassName.Equals(IIsWebServer))
                        {
                            RemoveFilter(Int32.Parse(child.Name), filterName);
                        }
                    }
                }
            }
            catch (Exception exp)
            {
                Console.WriteLine("\n     Error: " + exp.Message);
            }
        }

        internal static void AddFilter(int siteId, string filterName, string filterPath)
        {
            try
            {
                Console.Write("IIS: Add ISAPI Filter {0} to /LM/W3SVC/{1} ... ", 
                    filterName, siteId.ToString());

                DirectoryEntry webService = new DirectoryEntry(AdsPath_IisRoot);
                DirectoryEntry webServer = FindWebServer(webService, siteId);
                if (null == webServer)
                {
                    Console.WriteLine("\n     There is no web site with Id = " + siteId);
                    return;
                }

                DirectoryEntry webFilters = GetFiltersNode(webServer);
                RemoveFilterNode(webFilters, filterName);
                UnsetFilterName(webFilters, filterName);
                AddFilterNode(webFilters, filterName, filterPath);
                SetFilterName(webFilters, filterName);
                Console.WriteLine("done");
            }
            catch (Exception exp)
            {
                Console.WriteLine("\n     Error: " + exp.Message);
            }
        }

        internal static void RemoveFilter(int siteId, string filterName)
        {
            try
            {
                Console.Write("IIS: Remove ISAPI Filter {0} from /LM/W3SVC/{1} ... ",
                    filterName, siteId.ToString());

                DirectoryEntry webService = new DirectoryEntry(AdsPath_IisRoot);
                DirectoryEntry webServer = FindWebServer(webService, siteId);
                if (null == webServer)
                {
                    Console.WriteLine("\n     There is no web site with Id = " + siteId);
                    return;
                }

                DirectoryEntry webFilters = GetFiltersNode(webServer);
                RemoveFilterNode(webFilters, filterName);
                UnsetFilterName(webFilters, filterName);
                Console.WriteLine("done");
            }
            catch (Exception exp)
            {
                Console.WriteLine("\n     Error: " + exp.Message);
            }
        }

        private static DirectoryEntry FindWebServer(DirectoryEntry webService, int siteId)
        {
            foreach (DirectoryEntry child in webService.Children)
            {
                if (child.SchemaClassName.Equals(IIsWebServer) && child.Name.Equals(siteId.ToString()))
                {
                    return child;
                }
            }

            return null;
        }

        private static DirectoryEntry GetFiltersNode(DirectoryEntry entry)
        {
            foreach (DirectoryEntry child in entry.Children)
            {
                if (child.SchemaClassName.Equals(IIsFilters))
                {
                    return child;
                }
            }

            return entry.Invoke(AdsMethod_Create,
                new object[] { IIsFilters, "Filters" }) as DirectoryEntry;
        }

        private static void AddFilterNode(DirectoryEntry webFilters, string filterName, string filterPath)
        {
            DirectoryEntry webFilter = webFilters.Invoke(AdsMethod_Create,
                new object[] { IIsFilter, filterName }) as DirectoryEntry;

            webFilter.InvokeSet("FilterDescription", new object[] { filterName });
            webFilter.InvokeSet("FilterEnabled", new object[] { true });
            webFilter.InvokeSet("FilterPath", new object[] { filterPath });
            webFilter.CommitChanges();
            webFilter.Close();
        }

        private static void RemoveFilterNode(DirectoryEntry webFilters, string filterName)
        {
            foreach (DirectoryEntry webFilter in webFilters.Children)
            {
                if (webFilter.SchemaClassName.Equals(IIsFilter))
                {
                    if (webFilter.Name.ToLower().Equals(filterName.ToLower()))
                    {
                        webFilters.Invoke(AdsMethod_Delete,
                            new object[] { webFilter.SchemaClassName, webFilter.Name });

                        RemoveFilterNode(webFilters, filterName);
                        break;
                    }
                }
            }
        }

        private static void _SetFilterName(DirectoryEntry webFilters, string filterName, bool toAdd)
        {
            string loadOrder = webFilters.InvokeGet("FilterLoadOrder") as string;
            loadOrder = RemoveFromList(loadOrder, filterName);
            if (toAdd)
            {
                loadOrder = filterName + "," + loadOrder;
            }

            webFilters.InvokeSet("FilterLoadOrder",
                new object[] { loadOrder });
            webFilters.CommitChanges();
        }

        private static void SetFilterName(DirectoryEntry webFilters, string filterName)
        {
            _SetFilterName(webFilters, filterName, true);
        }

        private static void UnsetFilterName(DirectoryEntry webFilters, string filterName)
        {
            _SetFilterName(webFilters, filterName, false);
        }

        private static string RemoveFromList(string commaSeparatedList, string newElementName)
        {
            string[] elementArray = commaSeparatedList.Split(new char[] { ',' });
            string newCommaSeparatedList = "";
            foreach (string elementName in elementArray)
            {
                if (!String.IsNullOrEmpty(elementName) && !elementName.ToLower().Equals(newElementName.ToLower()))
                {
                    if (!String.IsNullOrEmpty(newCommaSeparatedList))
                    {
                        newCommaSeparatedList += ",";
                    }

                    newCommaSeparatedList += elementName;
                }
            }

            return newCommaSeparatedList;
        }
    }
}
